#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>

#include "cv.h"
#include "highgui.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/core/core.hpp"
#include "opencv/cv.hpp"
#include "windows.h"

using namespace cv;
using namespace std;
//using namespace cv::cuda;

#include <stdlib.h>

#include "max_min_kernel.hpp"
#include "omp.h"
int getMaxMin(uchar* buffIn, unsigned int countIn, uchar2 * max_minOut)
{
	uchar2 tempMaxMin = { buffIn[0], buffIn[0] };
	for (unsigned int i = 0; i < (countIn + 1) / 2; i++)
	{
		if (tempMaxMin.x > buffIn[i])
		{
			tempMaxMin.x = buffIn[i];
		}

		if (tempMaxMin.y < buffIn[i])
		{
			tempMaxMin.y = buffIn[i];
		}

		if (tempMaxMin.x > buffIn[countIn - i - 1])
		{
			tempMaxMin.x = buffIn[countIn - i - 1];
		}

		if (tempMaxMin.y < buffIn[countIn - i - 1])
		{
			tempMaxMin.y = buffIn[countIn - i - 1];
		}

	}

	max_minOut->x = tempMaxMin.x;
	max_minOut->y = tempMaxMin.y;
	return 0;
}

int getMaxMin_omp(uchar* buffIn, unsigned int countIn, uchar2 * max_minOut)
{
	uchar2 tempMaxMin = { buffIn[0], buffIn[0] };
#pragma omp parallel 
	{
#pragma omp for
		for (int i = 0; i < (countIn + 1) / 2; i++)
		{
			if (tempMaxMin.x > buffIn[i])
			{
				tempMaxMin.x = buffIn[i];
			}

			if (tempMaxMin.y < buffIn[i])
			{
				tempMaxMin.y = buffIn[i];
			}

			if (tempMaxMin.x > buffIn[countIn - i - 1])
			{
				tempMaxMin.x = buffIn[countIn - i - 1];
			}

			if (tempMaxMin.y < buffIn[countIn - i - 1])
			{
				tempMaxMin.y = buffIn[countIn - i - 1];
			}

		}

	}

	max_minOut->x = tempMaxMin.x;
	max_minOut->y = tempMaxMin.y;
	return 0;
}


double getCpuClock(void)
{
	LARGE_INTEGER freq;
	LARGE_INTEGER count;
	QueryPerformanceFrequency(&freq);

	QueryPerformanceCounter(&count);

	//cout << "freq:" << freq.QuadPart << ",count:" << count.QuadPart << endl;

	return ((double)count.QuadPart) / ((double)freq.QuadPart);
}


int main(int argc, char *argv[])
{
	if (argc < 2)
	{
		printf("No parameter for image file name!\n");
		return -1;
	}
	Mat image;
	image = imread(argv[1], IMREAD_GRAYSCALE);

	if (!image.data)
	{
		printf("No image data \n");
		return -1;
	}

	uchar * devBuff;
	int devBuffBlockNum;
	cudaMalloc(&devBuff, 1024*1024*20);
	cudaMemcpy(devBuff, image.data, image.cols*image.rows,cudaMemcpyHostToDevice);
	printf("width is %d, height is %d\n", image.cols, image.rows);

	double dStart = 0;
	double dEnd = 0;
	double sumTime = 0;
	double avgTime = 0;


	uchar2 maxMinResult = { 0, 0 };
	for (int i = 0; i < 20; i++)
	{
	
		dStart = getCpuClock();
	
		calc_max_min_1d(devBuff, image.cols, image.rows, devBuff + 1024 * 1024, &maxMinResult);
		dEnd = getCpuClock();
		sumTime = sumTime + dEnd - dStart;

	}
	printf("cuda max min cost %fs\n", sumTime / 20);
	printf("cuda max is %d,min is %d\n", maxMinResult.y, maxMinResult.x);
	


	uchar2 max_minResultHost = { 0, 0 };
	sumTime = 0;
	for (int i = 0; i < 20; i++)
	{
		dStart = getCpuClock();
		getMaxMin(image.data, image.cols*image.rows, &max_minResultHost);
		dEnd = getCpuClock();
		sumTime = sumTime + dEnd - dStart;
	}
	printf("host max min cost %fs\n", sumTime / 20);
	printf("host max is %d,min is %d\n", max_minResultHost.y, max_minResultHost.x);
	

	max_minResultHost.x = 0;
	max_minResultHost.y = 0;
	sumTime = 0;
	for (int i = 0; i < 20; i++)
	{
		dStart = getCpuClock();
		getMaxMin_omp(image.data, image.cols*image.rows, &max_minResultHost);
		dEnd = getCpuClock();
		sumTime = sumTime + dEnd - dStart;
	}
	printf("host omp max min cost %fs\n", sumTime / 20);
	printf("host omp max is %d,min is %d\n", max_minResultHost.y, max_minResultHost.x);
	

}
